package com.cskaoyan.wordmemorize.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cskaoyan.wordmemorize.auth.StpKit;
import com.cskaoyan.wordmemorize.converter.UserConverter;
import com.cskaoyan.wordmemorize.dao.entity.*;
import com.cskaoyan.wordmemorize.dao.mapper.*;
import com.cskaoyan.wordmemorize.dto.admin.LevelPrivilegeDTO;
import com.cskaoyan.wordmemorize.dao.entity.LevelDO;
import com.cskaoyan.wordmemorize.dao.entity.UserCheckinDO;
import com.cskaoyan.wordmemorize.dao.entity.UserDO;
import com.cskaoyan.wordmemorize.dao.mapper.LevelMapper;
import com.cskaoyan.wordmemorize.dao.mapper.UserCheckinMapper;
import com.cskaoyan.wordmemorize.dao.mapper.UserMapper;
import com.cskaoyan.wordmemorize.dto.admin.PageDTO;
import com.cskaoyan.wordmemorize.dto.app.*;
import com.cskaoyan.wordmemorize.request.PageRequest;
import com.cskaoyan.wordmemorize.request.UserRemindCommand;
import com.cskaoyan.wordmemorize.service.UserService;
import com.cskaoyan.wordmemorize.sms.SmsSender;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

import java.time.LocalDate;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Resource
    UserMapper userMapper;

    @Resource
    UserConverter userConverter;

    @Autowired
    UserRemindMapper userRemindMapper;

    @Autowired
    UserVocBoundStatisticsMapper userVocBoundStatisticsMapper;

    @Autowired
    UserVocBoundLogMapper userVocBoundLogMapper;

    @Autowired
    LevelMapper levelMapper;

    @Autowired
    LevelPrivilegeMapper levelPrivilegeMapper;

    @Autowired
    GoodsMapper goodsMapper;


    @Value("${user.remind.description}")
    String messageRemindDescription;

    @Autowired
    UserCheckinMapper userCheckinMapper;

    @Resource
    SmsSender smsSender;

    @Autowired
    UserRevStatisticsMapper userRevStatisticsMapper;


    // APP端获取用户信息
    @Override
    public UserDTO getUserInfo(Long userId) {
        // 查询基本信息
        UserDO userDO = userMapper.getUserInfo(userId);
        // 查询等级对应的特权码

        return userConverter.userDO2DTO(userDO);
    }

    // APP端获取用户的升级信息
    @Override
    public NextLevelDTO getUserNextDTO(Long userId) {
        // todo:存放累计升级所需的天数
        NextLevelDTO accumulatedNextLevelDTO = NextLevelDTO.builder()
                .signType(2)
                .build();
        // todo:存放连续升级所需的天数
        NextLevelDTO continuousNextLevelDTO = NextLevelDTO.builder()
                .signType(1)
                .build();
        // todo:获取用户当前等级
        LambdaQueryWrapper<LevelDO> levelQueryWrapper = new LambdaQueryWrapper<>();
        UserDO userDO = userMapper.selectById(userId);
        Integer levelValue = userDO.getLevelValue();
        levelQueryWrapper.eq(LevelDO::getLevelValue, levelValue);
        LevelDO nowLevelDO = levelMapper.selectOne(levelQueryWrapper);

        // todo:获取到下一等级的条件
        Integer nextLevelValue = levelValue + 1;
        levelQueryWrapper.clear();
        levelQueryWrapper.eq(LevelDO::getLevelValue, nextLevelValue);
        LevelDO nextLevelDO = levelMapper.selectOne(levelQueryWrapper);
        if (nextLevelDO == null) {
            // todo:无法获取到下一级的条件说明已满级,若满级,signType=1 remainDays=-1
            NextLevelDTO nextLevelDTO = NextLevelDTO.builder()
                    .remainDays(-1)
                    .signType(1)
                    .build();
            return nextLevelDTO;
        }
        // todo:checkInType为1表示下一级条件为连续打卡，2为累计打卡
        Integer checkInType = nextLevelDO.getCheckInType();
        // todo:checkInDays表示升级所需的签到天数
        Integer checkInDays = nextLevelDO.getCheckInDays();
        // todo:nowCheckInDays表示当前等级对应的签到天数
        Integer nowCheckInDays = nowLevelDO.getCheckInDays();
        // todo:若当前等级为6,当前等级对应的连续签到数为0
        if (6 == nowLevelDO.getLevelValue()) {
            nowCheckInDays = 0;
        }
        // todo:获取用户升级日期
        LocalDate levelDate = userDO.getLevelDate();

        // todo:对累计打卡情况进行判断
        if (2 == checkInType) {
            // todo:根据userId从user_checkin表中得到达到当前等级之后的签到天数(升级当天签到不算)accumulatedSignIn
            LambdaQueryWrapper<UserCheckinDO> userCheckinWrapper = new LambdaQueryWrapper<>();
            userCheckinWrapper.eq(UserCheckinDO::getUserId, userId)
                    .gt(UserCheckinDO::getSignDate, levelDate);
            List<UserCheckinDO> userCheckinDOS = userCheckinMapper.selectList(userCheckinWrapper);
            Integer accumulatedSignIn;
            // todo:若用户达到当前等级之后没签到,userCheckinDOS为null
            if (userCheckinDOS == null) {
                accumulatedSignIn = 0;
            } else {
                accumulatedSignIn = userCheckinDOS.size();
            }
            // todo:计算累计签到升级还需的天数
            accumulatedNextLevelDTO.setRemainDays(checkInDays - accumulatedSignIn - nowCheckInDays);
            return accumulatedNextLevelDTO;
        } else {

            // todo:对连续打卡情况进行判断
            LambdaQueryWrapper<UserCheckinDO> userCheckinWrapper = new LambdaQueryWrapper<>();
            LocalDate today = LocalDate.now();
            LocalDate yesterday = today.minusDays(1);
            userCheckinWrapper.eq(UserCheckinDO::getUserId, userId)
                    .eq(UserCheckinDO::getSignDate, today);
            UserCheckinDO todayUserCheckinDO = userCheckinMapper.selectOne(userCheckinWrapper);
            userCheckinWrapper.clear();
            userCheckinWrapper.eq(UserCheckinDO::getUserId, userId)
                    .eq(UserCheckinDO::getSignDate, yesterday);
            UserCheckinDO yesterdayUserCheckinDO = userCheckinMapper.selectOne(userCheckinWrapper);
            // todo:若昨天或今天存在打卡
            if (todayUserCheckinDO != null || yesterdayUserCheckinDO != null) {
                UserCheckinDO userCheckinDO = yesterdayUserCheckinDO;
                // todo:若今天打卡了
                if (todayUserCheckinDO != null) {
                    userCheckinDO = todayUserCheckinDO;
                }
                userCheckinWrapper.clear();
                userCheckinWrapper.eq(UserCheckinDO::getStickNo, userCheckinDO.getStickNo());
                List<UserCheckinDO> userCheckinDOS = userCheckinMapper.selectList(userCheckinWrapper);
                // todo:获取连续签到的起始日期
                Optional<UserCheckinDO> beginSignDate = userCheckinDOS.stream().reduce((a, b) -> {
                    if (a.getSignDate().isBefore(b.getSignDate())) {
                        return a;
                    } else {
                        return b;
                    }
                });
                UserCheckinDO beginUserCheckinDO = beginSignDate.get();
                // todo:如果当前连续签到的起始日期 > 用户当前等级升级日期+1, 则根据当前连续签到天数计算
                if (beginUserCheckinDO.getSignDate().isAfter(levelDate.plusDays(1))) {
                    continuousNextLevelDTO.setRemainDays(checkInDays - userCheckinDOS.size());
                    return continuousNextLevelDTO;
                }
                // todo:否则说明用户当前连续签到可以和用户当前等级的升级日期连起来
                // todo:那么就根据用户当前等级的连续签到数 + 从用户当前等级升级日期+1到今天或昨天为止的当前连续签到数
                if (6 == levelValue) {
                    // todo:此时还有一种特殊情况，用户已经6级，当前有几天连续签到，但还没到7级,直接用升级所需连续签到天数-当前连续签到天数
                    continuousNextLevelDTO.setRemainDays(checkInDays - userCheckinDOS.size());
                    return continuousNextLevelDTO;
                }
                List<UserCheckinDO> upLevelToNowSignDate = userCheckinDOS.stream().
                        filter(u -> u.getSignDate().isAfter(levelDate)).collect(Collectors.toList());
                continuousNextLevelDTO.setRemainDays(checkInDays - nowCheckInDays - upLevelToNowSignDate.size());
                return continuousNextLevelDTO;
            } else {
                // todo:若今天昨天都没打卡,则连续打卡天数为0
                continuousNextLevelDTO.setRemainDays(checkInDays);
                return continuousNextLevelDTO;
            }
        }
    }

    // APP端获取用户等级信息(包括已达到等级和未达到等级)
    @Override
    public DisplayUserLevelDTO getUserLevelInfo(Long userId) {

        // 获取用户当前等级
        LambdaQueryWrapper<UserDO> queryWrapper = new LambdaQueryWrapper<UserDO>().eq(UserDO::getId, userId);
        UserDO userDO = userMapper.selectOne(queryWrapper);
        Integer userLevel = userDO.getLevelValue();

        // --------nowLevel 获取当前用户已达到的等级及等级对应的特权信息-----------

        // 获取用户当前的等级信息 LevelDO
        LambdaQueryWrapper<LevelDO> levelQueryWrapper = new LambdaQueryWrapper<LevelDO>().eq(LevelDO::getLevelValue, userLevel);
        LevelDO levelDO = levelMapper.selectOne(levelQueryWrapper);

        // 获取小于等于用户当前等级对应的特权信息集合 List<LevelPrivilegeDO>
        LambdaQueryWrapper<LevelPrivilegeDO> levelPrivilegeQueryWrapper = new LambdaQueryWrapper<LevelPrivilegeDO>()
                .le(LevelPrivilegeDO::getLevelValue, userLevel);

        List<LevelPrivilegeDO> levelPrivilegeDOs = levelPrivilegeMapper.selectList(levelPrivilegeQueryWrapper);

        // 将LevelPrivilegeDOs转换为LevelPrivilegeDTOs
        List<LevelPrivilegeDTO> levelPrivilegeDTOs = userConverter.levelPrivilegeDOs2DTOs(levelPrivilegeDOs);

        // 如果用户等级为LVL0，即LavelValue为0，则goodsId为null
        String goodsId = null;

        if (userLevel != 0) {
            // 根据Type=2,以及当前用户等级获取对应的商品id
            LambdaQueryWrapper<GoodsDO> goodsQueryWrapper = new LambdaQueryWrapper<GoodsDO>()
                    .eq(GoodsDO::getValue, userLevel)
                    .eq(GoodsDO::getType, 2);

            GoodsDO goodsDO = goodsMapper.selectOne(goodsQueryWrapper);
            goodsId = String.valueOf(goodsDO.getId());
        }

        // 将当前用户已达到的等级及等级对应的特权信息封装成LevelPageDTO
        LevelPageDTO levelPageDTO = userConverter.levelDO2LevelPageDTO(levelDO);
        levelPageDTO.setLevelPrivileges(levelPrivilegeDTOs);
        levelPageDTO.setGoodsId(goodsId);

        // 新建一个名为 nowLevel 的 List<LevelPageDTO>，用来存储已有的LevelPageDTO
        List<LevelPageDTO> nowLevel = List.of(levelPageDTO);

        // -------upperLevel 获取当前用户未达到的等级及等级对应的特权信息--------
        // 获取用户未达到的等级信息集合 List<LevelDO>
        LambdaQueryWrapper<LevelDO> upperLevelQueryWrapper = new LambdaQueryWrapper<LevelDO>()
                .gt(LevelDO::getLevelValue, userLevel);

        List<LevelDO> upperLevelDOs = levelMapper.selectList(upperLevelQueryWrapper);
        // 将LevelDOs转换为LevelPageDTOs
        List<LevelPageDTO> upperLevelPageDTOsList = userConverter.levelDOsLevelPageDTOs(upperLevelDOs);

        // 获取用户未达到的等级的特权信息的集合 List<LevelPrivilegeDO>
        upperLevelPageDTOsList.forEach(levelPageDTO1 -> {
            LambdaQueryWrapper<LevelPrivilegeDO> upperLevelPrivilegeQueryWrapper = new LambdaQueryWrapper<LevelPrivilegeDO>()
                    .eq(LevelPrivilegeDO::getLevelValue, levelPageDTO1.getLevelValue());
            // 查询对应LevelValue的特权信息
            List<LevelPrivilegeDO> upperLevelPrivilegeDOs = levelPrivilegeMapper.selectList(upperLevelPrivilegeQueryWrapper);
            // 将LevelPrivilegeDOs转换为LevelPrivilegeDTOs
            List<LevelPrivilegeDTO> upperLevelPrivilegeDTOs = userConverter.levelPrivilegeDOs2DTOs(upperLevelPrivilegeDOs);
            levelPageDTO1.setLevelPrivileges(upperLevelPrivilegeDTOs);

            // 根据Type=2,以及当前用户等级获取对应的商品id
            LambdaQueryWrapper<GoodsDO> goodsQueryWrapper1 = new LambdaQueryWrapper<GoodsDO>()
                    .eq(GoodsDO::getValue, levelPageDTO1.getLevelValue())
                    .eq(GoodsDO::getType, 2);

            GoodsDO goodsDO1 = goodsMapper.selectOne(goodsQueryWrapper1);
            String goodsId1 = String.valueOf(goodsDO1.getId());
            levelPageDTO1.setGoodsId(goodsId1);
        });

        // 新建一个名为 upperLevel 的 List<LevelPageDTO>，用来存储未达到的LevelPageDTO
        List<LevelPageDTO> upperLevel = upperLevelPageDTOsList;

        // 新建一个名为 DisplayUserLevelDTO 的对象，用来存储已有的和未达到的LevelPageDTO
        DisplayUserLevelDTO displayUserLevelDTO = new DisplayUserLevelDTO();
        displayUserLevelDTO.setNowLevel(nowLevel);
        displayUserLevelDTO.setUpperLevel(upperLevel);

        return displayUserLevelDTO;
    }

    // APP端更新用户每天计划学习的单词量
    @Override
    public void updateVocCountOfDay(Long userId, Integer count) {
        // 构建更新条件
        UpdateWrapper<UserDO> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id", userId)
                .set("voc_count_of_day", count);

        // 更新数据库
        userMapper.update(null, updateWrapper);
    }

    // APP端更新用户的学习提醒时间
    @Override
    public void updateUserRemind(UserRemindCommand command) {

        // 转换
        UserRemindDO userRemindDO = userConverter.userRemindCommand2DO(command);

        // 获取正在访问的用户Id
        Long userId = StpKit.USER.getLoginIdAsLong();

        // 修改userRemindDO的userId
        userRemindDO.setUserId(userId);

        UpdateWrapper<UserRemindDO> updateWrapper = new UpdateWrapper<UserRemindDO>();
        updateWrapper.eq("user_id", userRemindDO.getUserId())
                .set("remind_time", userRemindDO.getRemindTime());

        // 更新数据库
        userRemindMapper.update(null, updateWrapper);

    }


    // APP端更获取用户的提醒信息
    @Override
    public UserRemindDTO getUserRemind(Long userId) {
        return null;
    }


    // -------------------------------------------------------


    // todo APP端发送消息提醒(在定时任务中调用该方法将待发送的提醒信息放入延迟队列)
    @Override
    public void messageRemind() {

        // 现在的时间 HH:mm:ss
        LocalTime now = LocalTime.now().withSecond(0).withNano(0);

        // 今天的日期 yyyy-MM-dd
//        String today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        LocalDate today = LocalDate.now();

        // 构建查询条件
        LambdaQueryWrapper<UserRemindDO> userRemindDOLambdaQueryWrapper = new LambdaQueryWrapper<>();

        // 找出没被删除、提醒时间等于现在 的对象
        userRemindDOLambdaQueryWrapper
                .eq(UserRemindDO::getIsDeleted, 0)
                .eq(UserRemindDO::getRemindTime, now);  // 精确匹配 remind_time == now（21:30:00）

        // 把结果装进特化数组
        List<UserRemindDO> userRemindDOs = userRemindMapper.selectList(userRemindDOLambdaQueryWrapper);


        // 遍历数组的对象，分别判断是否需要发短信
        for (UserRemindDO oneUserRemindDO : userRemindDOs) {

            // 从DO对象反射获取信息并发送
            // 电话号码去掉前4的"+86-"
            String phoneNumber = oneUserRemindDO.getPhoneNumber().substring(4);

            // 拿到userId
            Long userId = oneUserRemindDO.getUserId();

            // 通过userId拿到UserDO对象，再拿到自定义每日学习单词量
            Integer vocCountOfDay = userMapper.selectById(userId).getVocCountOfDay();

            // 通过userId拿到UserRevStatisticsDO对象，在拿到 复习的旧单词 和 复习的新单词
            LambdaQueryWrapper<UserRevStatisticsDO> userRevStatisticsDOLambdaQueryWrapper = new LambdaQueryWrapper<>();
            userRevStatisticsDOLambdaQueryWrapper
                    .eq(UserRevStatisticsDO::getUserId, userId)
                    .eq(UserRevStatisticsDO::getIsDeleted, 0);
            UserRevStatisticsDO userRevStatisticsDO = userRevStatisticsMapper.selectOne(userRevStatisticsDOLambdaQueryWrapper);

            // 拿到 复习的旧单词
            Integer vocNewCount = userRevStatisticsDO.getVocNewCount();

            // 拿到 复习的新单词
            Integer vocReviewCount = userRevStatisticsDO.getVocReviewCount();

            // 剩余需要学的单词 = 自定义每日学习单词量 - 复习的旧单词 - 复习的新单词
            Integer vocCount = vocCountOfDay - vocNewCount - vocReviewCount;


            // 判断是否已签到，如果今天已签到，则不发送信息
            LambdaQueryWrapper<UserCheckinDO> userCheckinDOLambdaQueryWrapper = new LambdaQueryWrapper<>();
            userCheckinDOLambdaQueryWrapper
                    .eq(UserCheckinDO::getUserId, userId)
                    .eq(UserCheckinDO::getSignDate, today)
                    .eq(UserCheckinDO::getType, 1);


            // 是否存在已打卡的记录
//            Boolean notYetCheckIn = userCheckinMapper.selectList(userCheckinDOLambdaQueryWrapper).isEmpty();
            Boolean alreadyCheckIn = userCheckinMapper.exists(userCheckinDOLambdaQueryWrapper);


            // 没打过卡就发短信
            if (!alreadyCheckIn) {
                // 传参并调用方法，发送短信
                smsSender.sendRemindMessage(null, vocCount, phoneNumber);
                log.info("已发送短信");

                // 沉睡15秒，因为一分钟只给发一条，避免被阿里云封
                try {
//                System.out.println("程序暂停 15 秒...");
                    Thread.sleep(15000); // 15 秒 = 15 * 1000 毫秒
//                System.out.println("15 秒已过，继续执行...");
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt(); // 重新设置中断状态
                }

            }


        }
    }


    // -----------------------------------------------------

    // APP端发获取用户的单词上限变化明细，如果未传分页信息则返回所有
    @Override
    public PageDTO<DisplayUserVocBoundLogDTO> getUserVocBoundLog(PageRequest pageRequest, Long userId) {

        // 构造查询条件
        QueryWrapper<UserVocBoundLogDO> userVocBoundLogDOQueryWrapper = new QueryWrapper<>();
        userVocBoundLogDOQueryWrapper.eq("user_id", userId);

        // 检查分页参数,如果没有指定分页参数，则查询全部
        if (pageRequest.getPageNum() == null) {
            List<UserVocBoundLogDO> userVocBoundLogDOs = userVocBoundLogMapper.selectList(userVocBoundLogDOQueryWrapper);
            List<DisplayUserVocBoundLogDTO> displayUserVocBoundLogDTOS = userConverter.userVocBoundLogDTOs2DisplayDTOs(userVocBoundLogDOs);
            PageDTO<DisplayUserVocBoundLogDTO> pageDTO = userConverter.userVocBoundLogPage2PageDTO(displayUserVocBoundLogDTOS
                    , (long) displayUserVocBoundLogDTOS.size());
            return pageDTO;
        }

        // 有分页参数，查询分页数据
        // 构造分页对象
        Page<UserVocBoundLogDO> pageParam = new Page<>(pageRequest.getPageNum(), pageRequest.getCount());

        // 执行查询
        Page<UserVocBoundLogDO> resultPage = userVocBoundLogMapper.selectPage(pageParam, userVocBoundLogDOQueryWrapper);

        long total = resultPage.getTotal();
        List<UserVocBoundLogDO> pageList = resultPage.getRecords();

        List<DisplayUserVocBoundLogDTO> displayUserVocBoundLogDTOS = userConverter.userVocBoundLogDTOs2DisplayDTOs(pageList);
        PageDTO<DisplayUserVocBoundLogDTO> pageDTO = userConverter.userVocBoundLogPage2PageDTO(displayUserVocBoundLogDTOS, total);

        return pageDTO;
    }

    // APP端获取用户的单词上限统计信息
    @Override
    public UserVocBoundStatisticsDTO getUserVocBoundStatistics(Long userId) {

        // 获取用户的单词上限统计信息
        QueryWrapper<UserVocBoundStatisticsDO> userVocBoundStatisticsDOQueryWrapper = new QueryWrapper<UserVocBoundStatisticsDO>()
                .eq("user_id", userId);

        UserVocBoundStatisticsDO userVocBoundStatisticsDO = userVocBoundStatisticsMapper.selectOne(userVocBoundStatisticsDOQueryWrapper);
        // 转换
        UserVocBoundStatisticsDTO userVocBoundStatisticsDTO = userConverter.userVocBoundStatisticsDO2DisplayDTO(userVocBoundStatisticsDO);

        return userVocBoundStatisticsDTO;
    }

    // APP端获取用户我的页面展示的信息
    @Override
    public UserPageDTO getUserPage(Long userId) {
        // 查询每天规划单词学习量和补签卡数量
        QueryWrapper<UserDO> userDOQueryWrapper = new QueryWrapper<UserDO>()
                .eq("id", userId)
                .select("voc_count_of_day", "has_sign_card");

        UserDO userDO = userMapper.selectOne(userDOQueryWrapper);
        Integer vocCountOfDay = userDO.getVocCountOfDay();
        Integer hasSignCard = userDO.getHasSignCard();

        // 查询每日提醒学习时间
        QueryWrapper<UserRemindDO> userRemindDOQueryWrapper = new QueryWrapper<UserRemindDO>()
                .eq("user_id", userId)
                .select("remind_time")
                .last("LIMIT 1");

        UserRemindDO userRemindDO = userRemindMapper.selectOne(userRemindDOQueryWrapper);
        String remindTime = String.valueOf(userRemindDO.getRemindTime());

        // 查询总的单词上限数量、免费获取的单词上限数量和购买的单词上限数量
        QueryWrapper<UserVocBoundStatisticsDO> vocBoundStatisticsDOQueryWrapper = new QueryWrapper<UserVocBoundStatisticsDO>()
                .eq("user_id", userId)
                .select("total", "pay", "free");

        UserVocBoundStatisticsDO userVocBoundStatisticsDO = userVocBoundStatisticsMapper.selectOne(vocBoundStatisticsDOQueryWrapper);
        Integer total = userVocBoundStatisticsDO.getTotal();
        Integer free = userVocBoundStatisticsDO.getFree();
        Integer pay = userVocBoundStatisticsDO.getPay();

        // 构建UserPageDTO
        UserPageDTO userPageDTO = new UserPageDTO();
        userPageDTO.setVocCountOfDay(vocCountOfDay);
        userPageDTO.setTotalVocBound(total);
        userPageDTO.setFreeVocBound(free);
        userPageDTO.setPayVocBound(pay);
        userPageDTO.setReSignCard(hasSignCard);
        userPageDTO.setRemindTime(remindTime);

        return userPageDTO;
    }

    // todo 用户鉴权
    @Override
    public Set<String> getPermissionCode(Long userId) {
        return userMapper.getUserPermissionCodes(userId).stream().filter(codes -> codes != null).collect(Collectors.toSet());
    }
}
